/**
 * Copyright (c) 2008, Andrew Carter
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer. Redistributions in binary form must reproduce 
 * the above copyright notice, this list of conditions and the following disclaimer in 
 * the documentation and/or other materials provided with the distribution. Neither the 
 * name of Andrew Carter nor the names of contributors may be used to endorse or promote 
 * products derived from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.acarter.jmejtree;

import java.util.ArrayList;
import java.util.Vector;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import com.acarter.jmejtree.util.E_RenderState;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.PassNode;
import com.jme.scene.PassNodeState;
import com.jme.scene.Spatial;
import com.jme.scene.state.RenderState;

/**
 * @deprecated use <code>JMEComposableTreeModel</code> instead
 * @author Andrew Carter
 */
public class JMEJTreeModel implements TreeModel {

	/**	 */
	private Node rootNode = null;

	private Vector<TreeModelListener> listeners;

	/**
	 * Returns the root of the tree. Returns null only if the tree has no nodes.
	 * 
	 * @param rootNode
	 */
	public JMEJTreeModel(Node rootNode) {

		this.rootNode = rootNode;
		listeners = new Vector<TreeModelListener>();
	}

	/**
	 * Returns the child of parent at index index in the parent's child array.
	 * parent must be a node previously obtained from this data source. This
	 * should not return null if index is a valid index for parent (that is
	 * index >= 0 && index < getChildCount(parent)).
	 */
	public Object getChild(Object parent, int index) {

		if (parent instanceof Spatial) {

			int ctrlCount = getControllerCount((Spatial) parent);
			int rsCount = getRenderStateCount((Spatial) parent);
			
			int		li_passes = getPassNodeStateCount((Spatial)parent);
			
			if (index >= 0) {

				// Get a controller
				if (index < ctrlCount) {
					return getController((Spatial) parent, index);
				}
				// Get a render state
				else if ((index >= ctrlCount) && (index < (rsCount + ctrlCount))) {
					return getRenderState((Spatial) parent, index - ctrlCount);
				}
				// get a pass node state
				else if((index >= (rsCount + ctrlCount)) && (index < (rsCount + ctrlCount + li_passes))) {
					return getPassNodeState((Spatial)parent, index - (rsCount + ctrlCount));
				}
				// Get an actual child node
				else if (parent instanceof Node) {
					if ((index >= (rsCount + ctrlCount + li_passes)) && (index < getChildCount(parent)))
						return ((Node) parent).getChild(index - (rsCount + ctrlCount + li_passes));
				}
			}
		}
		else if(parent instanceof PassNodeState) {
			return getRenderStateFromPassNodeState((PassNodeState)parent, index);
		}

		return null;
	}

	/**
	 * Returns the number of children of parent. Returns 0 if the node is a leaf
	 * or if it has no children. parent must be a node previously obtained from
	 * this data source.
	 */
	public int getChildCount(Object parent) {
		
		if (parent instanceof Spatial) {

			int children = getControllerCount((Spatial) parent);
			children += getRenderStateCount((Spatial) parent);
			
			if(parent instanceof PassNode) {
				children += ((PassNode)parent).nrPasses();
			}
			
			if (parent instanceof Node)
				children += ((Node) parent).getQuantity();

			return children;
		}
		else if (parent instanceof PassNodeState) {
			return getRenderStateCountFromPassNodeState((PassNodeState)parent);
		}

		return 0;
	}

	/**
	 * Returns the index of child in parent. If either parent or child is null,
	 * returns -1. If either parent or child don't belong to this tree model,
	 * returns -1.
	 */
	public int getIndexOfChild(Object parent, Object child) {

		if ((parent != null) && (child != null)) {

			if (parent instanceof Spatial) {

				int ctrlCount = getControllerCount((Spatial) parent);
				int rsCount = getRenderStateCount((Spatial) parent);
				int	li_passNodeStateCnt = getPassNodeStateCount((Spatial)parent);
				
				if(child instanceof Controller)
					return getControllerChildIndex((Spatial) parent, (Controller) child);
				
				else if (child instanceof RenderState)
					return getRenderStateChildIndex((Spatial) parent, (RenderState) child) + ctrlCount;
				
				else if(child instanceof PassNodeState)
					return getPassNodeStateChildIndex((Spatial)parent, (PassNodeState)child) + rsCount + ctrlCount; 
				
				else if (parent instanceof Node) {
					if (child instanceof Spatial)
						return ((Node) parent).getChildIndex((Spatial) child) + rsCount + ctrlCount + li_passNodeStateCnt;
				}
			}
			else if(parent instanceof PassNodeState) {
				if(child instanceof RenderState) {
					return getRenderStateChildIndexFromPassNodeState((PassNodeState)parent, (RenderState)child);
				}
				
			}
		}

		return -1;
	}

	/**
	 * 
	 * @param child
	 * @return
	 */
	public Object getParent(Object child) {

		if (child instanceof Spatial)
			return ((Spatial) child).getParent();

		return null;
	}

	/**
	 * 
	 * @param object
	 * @return
	 */
	public TreePath getPathToRoot(Object object) {
		
		if(object == null)
			return null;

		TreePath path = new TreePath(rootNode);

		ArrayList<Object> list = new ArrayList<Object>();

		list.add(0, object);

		while ((object = getParent(object)) != null) {

			if (object.equals(rootNode))
				break;

			list.add(0, object);
		}

		for (Object child : list)
			path = path.pathByAddingChild(child);

		return path;
	}

	/**
	 * Returns the root of the tree. Returns null only if the tree has no nodes.
	 */
	public Object getRoot() {

		return rootNode;
	}

	/**
	 * Returns true if node is a leaf. It is possible for this method to return
	 * false even if node has no children. A directory in a file system, for
	 * example, may contain no files; the node representing the directory is not
	 * a leaf, but it also has no children.
	 */
	public boolean isLeaf(Object object) {

		return getChildCount(object) > 0 ? false : true;
	}

	/**
	 * Adds a listener for the TreeModelEvent posted after the tree changes.
	 */
	public void addTreeModelListener(TreeModelListener l) {

		listeners.add(l);
	}

	/**
	 * Removes a listener previously added with addTreeModelListener.
	 */
	public void removeTreeModelListener(TreeModelListener l) {

		listeners.remove(l);
	}

	/**
	 * 
	 * @param e
	 */
	public void fireTreeNodesChanged(TreeModelEvent e) {

		for (TreeModelListener l : listeners)
			l.treeNodesChanged(e);
	}

	/**
	 * 
	 * @param e
	 */
	public void fireTreeNodesInserted(TreeModelEvent e) {

		for (TreeModelListener l : listeners)
			l.treeNodesInserted(e);
	}

	/**
	 * 
	 * @param e
	 */
	public void fireTreeNodesRemoved(TreeModelEvent e) {

		for (TreeModelListener l : listeners)
			l.treeNodesRemoved(e);
	}

	/**
	 * 
	 * @param e
	 */
	public void fireTreeStructureChanged(TreeModelEvent e) {

		for (TreeModelListener l : listeners)
			l.treeStructureChanged(e);
	}

	/**
	 * Messaged when the user has altered the value for the item identified by
	 * path to newValue. If newValue signifies a truly new value the model
	 * should post a treeNodesChanged event.
	 */
	public void valueForPathChanged(TreePath path, Object newValue) {

	}

	/**
	 * Counts the number of controllers currently attached to the given spatial.
	 * 
	 * @param spatial
	 * @return
	 */
	private int getControllerCount(Spatial spatial) {

		int count = 0;

		if (spatial != null)
			count = spatial.getControllerCount();

		return count;
	}

	/**
	 * Returns the index of the controller as it is represented in the tree.
	 * 
	 * @param spatial
	 *            The parent object
	 * @param controller
	 *            The controller
	 * @return Index of the controller in the tree
	 */
	private int getControllerChildIndex(Spatial spatial, Controller controller) {

		int index = -1;

		if (spatial != null) {

			for (Controller c : spatial.getControllers()) {

				index++;
				
				if (c.equals(controller))
					break;
			}
		}

		return index;
	}
	

	/**
	 * Returns the controller of the parent given the index from the tree.
	 * 
	 * @param spatial
	 *            Parent object
	 * @param index
	 *            Index in tree of the controller
	 * @return The controller, or null
	 */
	private Controller getController(Spatial spatial, int index) {
		return (spatial != null) ? spatial.getController(index) : null;
	}
	
	private	int	getPassNodeStateCount(Spatial cl_spatial) {
		if(cl_spatial instanceof PassNode) {
			return ((PassNode)cl_spatial).nrPasses();
		}
		
		return 0;
	}
	
	private int getPassNodeStateChildIndex(Spatial spatial, PassNodeState cl_passNodeState) {

		if(spatial instanceof PassNode) {
			PassNode		lcl_pn = (PassNode)spatial;
			
			for(int li_index = 0; li_index < lcl_pn.nrPasses(); li_index++) {
				if(cl_passNodeState.equals(lcl_pn.getPass(li_index))) {
					return li_index;
				}
			}
		}
		
		return -1;
	}
	
	private	PassNodeState	getPassNodeState(Spatial cl_sceneElement, int i_index) {
		if(cl_sceneElement instanceof PassNode) {
			return ((PassNode)cl_sceneElement).getPass(i_index);
		}
		
		return null;
	}

	/**
	 * Counts the number of render states currently defined on the given spatial.
	 * 
	 * @param spatial
	 *            Spatial to look into
	 * @return Number of defined render states
	 */
	private int getRenderStateCount(Spatial spatial) {

		int count = 0;

		if (spatial != null) {

			for (E_RenderState rs : E_RenderState.values()) {

				if (spatial.getRenderState(rs.getType()) != null)
					count++;
			}
		}

		return count;
	}
	
	

	/**
	 * Returns the index of the render state as it is represented in the tree.
	 * 
	 * @param sceneElement
	 *            The parent object
	 * @param renderState
	 *            The renderstate
	 * @return Index of the render state in the tree
	 */
	private int getRenderStateChildIndex(Spatial sceneElement, RenderState renderState) {

		int index = -1;

		if (sceneElement != null) {

			for (E_RenderState rs : E_RenderState.values()) {

				if (sceneElement.getRenderState(rs.getType()) != null)
					index++;

				if (renderState.getType() == rs.getType())
					break;
			}
		}

		return index;
	}

	
	/**
	 * Returns the render state of the parent given the index from the tree.
	 * 
	 * @param sceneElement
	 *            Parent object
	 * @param index
	 *            Index in tree of the render state
	 * @return The render state, or null
	 */
	private RenderState getRenderState(Spatial sceneElement, int index) {

		int curIndex = -1;

		if (sceneElement != null) {

			for (E_RenderState rs : E_RenderState.values()) {

				RenderState state = sceneElement.getRenderState(rs.getType());

				if (state != null) {
					curIndex++;

					if (curIndex == index)
						return state;
				}
			}
		}

		return null;
	}
	
	/**
	 * PassNodeState stuff
	 * @param sceneElement
	 * @param index
	 * @return
	 */
	private RenderState getRenderStateFromPassNodeState(PassNodeState sceneElement, int index) {

		int curIndex = -1;

		if (sceneElement != null) {

			for (E_RenderState rs : E_RenderState.values()) {

				RenderState state = sceneElement.getPassState(rs.getType());

				if (state != null) {
					curIndex++;

					if (curIndex == index)
						return state;
				}
			}
		}

		return null;
	}
	
	private int getRenderStateCountFromPassNodeState(PassNodeState spatial) {

		int count = 0;

		if (spatial != null) {

			for (E_RenderState rs : E_RenderState.values()) {

				if (spatial.getPassState(rs.getType()) != null)
					count++;
			}
		}

		return count;
	}
	
	private int getRenderStateChildIndexFromPassNodeState(PassNodeState sceneElement, RenderState renderState) {

		int index = -1;

		if (sceneElement != null) {

			for (E_RenderState rs : E_RenderState.values()) {

				if (sceneElement.getPassState(rs.getType()) != null)
					index++;

				if (renderState.getType() == rs.getType())
					break;
			}
		}

		return index;
	}
}
